package ai.fabu.dataplatform.service.impl;

import ai.fabu.dataplatform.bean.bagdata.BagData;
import ai.fabu.dataplatform.bean.framedata.FrameData;
import ai.fabu.dataplatform.bean.front.BagFrontData;
import ai.fabu.dataplatform.bean.front.GetFrontData;
import ai.fabu.dataplatform.bean.groupdata.GroupData;
import ai.fabu.dataplatform.bean.groupdata.MapNameData;
import ai.fabu.dataplatform.bean.groupdata.VehicleNameData;
import ai.fabu.dataplatform.bean.metadata.GetMetadata;
import ai.fabu.dataplatform.bean.task.GroupDataTask;
import ai.fabu.dataplatform.bean.topicdata.TopicData;
import ai.fabu.dataplatform.bean.util.IndexData;
import ai.fabu.dataplatform.bean.util.Pair;
import ai.fabu.dataplatform.constant.ConstantValue;
import ai.fabu.dataplatform.constant.ResultCode;
import ai.fabu.dataplatform.help.Result;
import ai.fabu.dataplatform.service.ApiForFrontService;
import ai.fabu.dataplatform.service.ApiForTaskService;
import ai.fabu.dataplatform.service.ApiService;
import ai.fabu.dataplatform.utils.*;
import ai.fabu.dataplatform.vo.groupdata.GroupDataGetVO;
import ai.fabu.dataplatform.vo.groupdata.GroupDataQueryVO;
import ai.fabu.dataplatform.vo.metadata.UpdateMetadataVO;
import ai.fabu.dataplatform.vo.task.GroupDataTaskImportVO;
import ai.fabu.dataplatform.vo.task.GroupDataTaskResetVO;
import com.mongodb.MongoException;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.io.File;
import java.util.*;

import static ai.fabu.dataplatform.constant.ConstantName.*;
import static ai.fabu.dataplatform.constant.ConstantValue.*;
import static ai.fabu.dataplatform.utils.HttpUtil.doGet;

@Slf4j
@Service
public class ApiForFrontServiceImpl implements ApiForFrontService {
    @Autowired
    private MongoOperations mongoOperations;
    @Autowired
    private ApiService apiService;
    @Autowired
    private ApiForTaskService apiForTaskService;


    @Value("${bag.data.save.path}")
    private String bagSavePath;

    private static final String PATH_SYMBOL_SLASH = "/";



    /**
     * 时间去转换，把一个时间戳转换为一个 yyyyMMddHHmm 的字符串
     * @param beginTime
     * @return
     */

    public String translateBeginTime(long beginTime) {
        String date = String.valueOf(beginTime);
        return TimeUtil.timeStamp2Date(date, "yyyyMMddHHmm");
    }


    @Override
    public Result<List<String>> getMapNameOrVehicleNameAll(String name, int type) {
        Result<List<String>> result = new Result<>();
        Query query = new Query();
        query.fields().include(name);
        switch (type){
            case MAP_TYPE:
                List<MapNameData> mapNameDataList = mongoOperations.find(query, MapNameData.class);
                List<String> mapNameList = new ArrayList<>();
                mapNameDataList.forEach(mapNameData -> mapNameList.add(mapNameData.getMapName()));
                result.returnWithValue(ResultCode.SUCCESS, mapNameList);
                break;
            case VEHICLE_TYPE:
                List<VehicleNameData> vehicleNameDataList = mongoOperations.find(query, VehicleNameData.class);
                List<String> vehicleNameList = new ArrayList<>();
                vehicleNameDataList.forEach(vehicleNameData -> vehicleNameList.add(vehicleNameData.getVehicleName()));
                result.returnWithValue(ResultCode.SUCCESS, vehicleNameList);
                break;
            default:
                log.error("type error");
                result.returnWithoutValue(ResultCode.KIDDING_ERROR);
        }
        return result;
    }

    /**
     * 导入包，把该任务加入任务表中去，并把相应的 map name 和 vehicle name 放到数据库中去
     * @param groupDataTaskImportVO
     * @return
     */

    @Retryable(
            value = {UncategorizedMongoDbException.class, MongoException.class},
            maxAttempts = 5,
            backoff = @Backoff(delay = 100L, maxDelay = 3000L, random = true)
    )
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result importGroupAsTask(GroupDataTaskImportVO groupDataTaskImportVO) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group path: {}, time: {}", methodName, groupDataTaskImportVO.getGroupPath(), groupDataTaskImportVO.getTime());
        Result result = new Result();
        String time = groupDataTaskImportVO.getTime();
        String groupPath = groupDataTaskImportVO.getGroupPath();
        if(time == null || groupPath == null){
            log.error("{}: begin time or group path is null", methodName);
            result.returnWithoutValue(ResultCode.LACK_PARAM_ERROR);
            return result;
        }
        if (!groupPath.endsWith(PATH_SYMBOL_SLASH)) {
            groupPath = groupPath + PATH_SYMBOL_SLASH;
        }
        Map<String, String> frontMetadata = groupDataTaskImportVO.getMetadata();



        //车辆验证，必须写入车辆
        String mapName = frontMetadata.get(MAP_NAME);
        String vehicleName = frontMetadata.get(VEHICLE_NAME);
        if (mapName.isEmpty() || vehicleName.isEmpty()) {
            log.error("{}: map_name or vehicle_name is null", methodName);
            result.returnWithoutValue(ResultCode.LACK_PARAM_ERROR);
            return result;
        }
        //导入判重 从 groupDataTask 表中 判断
        Query query = new Query();
        query.addCriteria(Criteria.where(GROUP_PATH).is(groupPath));
//        if (mongoOperations.exists(query, GroupDataTask.class)) {
//            log.warn("{}: group task has already imported!", methodName);
//            result.returnWithoutValue(ResultCode.REPEAT_IMPORT_ERROR);
//            return result;
//        }

        // 导入判重 从 groupData 表中 判断
        if(mongoOperations.exists(query, GroupData.class)){
            log.warn("{}: group has already imported!", methodName);
            result.returnWithoutValue(ResultCode.REPEAT_IMPORT_ERROR);
            return result;
        }

        //判断路径是否正确
        File groupDir = new File(groupPath);
        if(!groupDir.exists()){
            result.returnWithoutValue(ResultCode.NO_PATH_ERROR);
            return result;
        }
        if (!groupDir.isDirectory()) {
            result.returnWithoutValue(ResultCode.NO_DIRECTORY_ERROR);
            return result;
        }

        // 地图 车辆名字不存在 则插入
        query = new Query();
        query.addCriteria(Criteria.where(MAP_NAME).is(mapName));
        if(!mongoOperations.exists(query, MapNameData.class)){
            MapNameData mapNameData = MapNameData.staticMapNameData(mapName);
            mongoOperations.insert(mapNameData);
        }
        query = new Query();
        query.addCriteria(Criteria.where(VEHICLE_NAME).is(vehicleName));
        if(!mongoOperations.exists(query, VehicleNameData.class)){
            VehicleNameData vehicleNameData = VehicleNameData.staticVehicleNameData(vehicleName);
            mongoOperations.insert(vehicleNameData);
        }

        String groupName = mapName + "_" + vehicleName + "_" + time;

        Map<String, byte[]> metadata = new HashMap<>();
        frontMetadata.forEach((key, value) -> metadata.put(key, value.getBytes()));
        //插入表
        GroupDataTask groupDataTask = GroupDataTask.staticGroupDataTask(groupName, groupPath, mapName, vehicleName, PROCESS_NOT_START_STATUS, groupDataTaskImportVO.getUploader());
        mongoOperations.insert(groupDataTask);
        GroupData groupData = GroupData.staticGroupData(groupName, groupPath, metadata);
        mongoOperations.insert(groupData);
        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }



    /**
     * 给定一个 group path 读取 该目录下的 README 文件
     * @param groupPath
     * @return
     */

    @Override
    public Result<String> readGroupREADME(String groupPath) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        if(!groupPath.endsWith("/"))  groupPath += "/";
        log.info("{}: group path: {}", methodName, groupPath);
        String readMeFile = FileUtil.readFileByPath(groupPath + "README");
        String meta = FileUtil.readFileByPath(groupPath + "meta.ini");
        Result<String> result = new Result<>();
        if(readMeFile == null && meta == null){
            result.returnWithoutValue(ResultCode.NO_DATA);
            return result;
        }
        if(meta == null){
            result.returnWithValue(ResultCode.SUCCESS, readMeFile);
            return result;
        }
        if(readMeFile == null){
            result.returnWithValue(ResultCode.SUCCESS, meta);
            return result;
        }
        result.returnWithValue(ResultCode.SUCCESS, readMeFile + "\n" + meta);
        return result;
    }

    @Override
    public Result<List<GroupDataTask>> getGroupDataTaskAll() {
        Result<List<GroupDataTask>> result = new Result<>();
        List<GroupDataTask> groupDataTaskList = mongoOperations.findAll(GroupDataTask.class);
        result.returnWithValue(ResultCode.SUCCESS, groupDataTaskList);
        return result;
    }

    /**
     * 给定开始时间和结束时间，把在这个区间内的任务查询出来
     * @param beginTime
     * @param endTime
     * @return
     */

    @Override
    public Result<List<GroupDataTask>> monitor(Long beginTime, Long endTime) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: begin time: {}, end time: {}", methodName, beginTime, endTime);
        Result<List<GroupDataTask>> result = new Result<>();
        if(beginTime == null && endTime == null)  return getGroupDataTaskAll();
        Query query = new Query();
        if (beginTime == null)  beginTime = ConstantValue.BEGIN_TIME;
        if(endTime == null)  endTime = ConstantValue.END_TIME;
        query.addCriteria(Criteria.where(UPDATE_TIME).gte(beginTime).lte(endTime));
        result.returnWithValue(ResultCode.SUCCESS, mongoOperations.find(query, GroupDataTask.class));
        return result;
    }



    // status 2
    public Result resetGroupDataTaskAll(String groupName){
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Result result = new Result();
        Query query = new Query();
        query.addCriteria(Criteria.where(GROUP_NAME).is(groupName));
        mongoOperations.remove(query, BagData.class);
        mongoOperations.remove(query, TopicData.class);
        mongoOperations.remove(query, FrameData.class);

//        BulkOperations bagBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, BagData.class);
//        bagBulkOperations.remove(query).execute();
//        BulkOperations topicBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, TopicData.class);
//        topicBulkOperations.remove(query).execute();
//        BulkOperations frameBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, FrameData.class);
//        frameBulkOperations.remove(query).execute();


        query = new Query();
        query.addCriteria(Criteria.where(GROUP_NAME).is(groupName)).addCriteria(Criteria.where(COMPLETE_STATUS).is(PROCESSED_STATUS));
        Update update = new Update();
        update.set(COMPLETE_STATUS, PROCESS_NOT_START_STATUS);
        update.set(SOURCE, UN_SET);
        update.set(MAP_STATUS, PROCESS_NOT_START_STATUS);
        update.set(DONE_BAG_NUM, 0);
        update.set(TOTAL_BAG_NUM, 0);
        update.set(CURRENT_BAG_NAME, STRING_DEFAULT);
        update.set(UPDATE_TIME, TimeUtil.getNowTimestampLong());
        UpdateResult updateResult = mongoOperations.updateFirst(query, update, GroupDataTask.class);
        if(updateResult.getModifiedCount() != 0){
//            result.returnWithoutValue(completeStatus == PROCESS_NOT_START_STATUS ? ResultCode.SUCCESS : ResultCode.NO_BAG_ERROR);
            result.returnWithoutValue(ResultCode.SUCCESS);
        }
        else {
            result.returnWithoutValue(ResultCode.PARAM_ERROR);
        }
        return result;
    }

    // status 3
    public Result resetGroupDataTaskPart(String groupName, byte completeStatus){
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Result result = new Result();
        Query query = new Query();
        query.addCriteria(Criteria.where(GROUP_NAME).is(groupName)).addCriteria(Criteria.where(COMPLETE_STATUS).is(completeStatus));
        query.fields().include(CURRENT_BAG_NAME);
        Update update = new Update();
        update.set(COMPLETE_STATUS, PROCESS_NOT_START_STATUS);
        update.set(CURRENT_BAG_NAME, STRING_DEFAULT);
        update.set(SOURCE, UN_SET);
        update.set(UPDATE_TIME, TimeUtil.getNowTimestampLong());
        GroupDataTask groupDataTask = mongoOperations.findAndModify(query, update, GroupDataTask.class);

//        GroupDataTask groupDataTask = mongoOperations.findOne(query, GroupDataTask.class);
        if(groupDataTask == null){
            log.error("{}: group data task is null", methodName);
            result.returnWithoutValue(ResultCode.PARAM_ERROR);
            return result;
        }
        query = new Query();
        String currentBagName = groupDataTask.getCurrentBagName();
        if(currentBagName == null || currentBagName.equals(STRING_DEFAULT)){
            log.info("{}: no data to delete", methodName);
        }
        else{
            query.addCriteria(Criteria.where(GROUP_NAME).is(groupName)).addCriteria(Criteria.where(BAG_NAME).is(currentBagName));
            mongoOperations.remove(query, BagData.class);
            mongoOperations.remove(query, TopicData.class);
            mongoOperations.remove(query, FrameData.class);
//            BulkOperations bagBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, BagData.class);
//            bagBulkOperations.remove(query).execute();
//            BulkOperations topicBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, TopicData.class);
//            topicBulkOperations.remove(query).execute();
//            BulkOperations frameBulkOperations = mongoOperations.bulkOps(BulkOperations.BulkMode.UNORDERED, FrameData.class);
//            frameBulkOperations.remove(query).execute();
        }
        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }



    /**
     * 重置 group data task
     * @param groupDataTaskResetVO
     * @return
     */


    @Override
    public Result resetGroupDataTask(GroupDataTaskResetVO groupDataTaskResetVO) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: {}", methodName, groupDataTaskResetVO);
        int completeStatus = groupDataTaskResetVO.getCompleteStatus();
        Result result = new Result();
        if(groupDataTaskResetVO.getGroupName().isEmpty()){
            log.error("{}: group name is empty", methodName);
            result.returnWithoutValue(ResultCode.PARAM_ERROR);
            return result;
        }
        if(completeStatus < PROCESSING_STATUS || completeStatus > PROCESS_FAILURE_STATUS){
            log.error("{}: complete status is invalid", methodName);
            result.returnWithoutValue(ResultCode.PARAM_ERROR);
            return result;
        }
        if(completeStatus == PROCESSED_STATUS)  return resetGroupDataTaskAll(groupDataTaskResetVO.getGroupName());
        return resetGroupDataTaskPart(groupDataTaskResetVO.getGroupName(), groupDataTaskResetVO.getCompleteStatus());
    }



    @Override
    public Result<List<GetFrontData>> getDataByKeyList(GroupDataGetVO groupDataGetVO) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: {}", methodName, groupDataGetVO);
        if(!groupDataGetVO.getKeyList().isEmpty())
            groupDataGetVO.getKeyList().add(LOCALIZATION_IMAGE_PATH);
        Result<List<GetFrontData>> result = new Result<>();
        IndexData indexData = groupDataGetVO.getIndexData();
        Result<List<GetMetadata>> metadatasResult = apiService.getMetadata(indexData, GROUP_TYPE, groupDataGetVO.getKeyList());
        if(ResultCode.SUCCESS.getCode() != metadatasResult.getCode()){
            log.error("{}: query group fail", methodName);
            result.setCode(metadatasResult.getCode());
            result.setMsg(metadatasResult.getMsg());
            return result;
        }
        // 构造一个最后结构的 map
        Map<String, GetFrontData> data = new HashMap<>();
        List<GetMetadata> metadataList = metadatasResult.getData();

        for (GetMetadata getMetadata : metadataList) {
            Map<String, String> metadata = new HashMap<>();
            byte[] localizationImage = null;
            List<Pair<String, byte[]>> dataList = getMetadata.getData();
            for (Pair<String, byte[]> pairData : dataList) {
                if(pairData.getKey().equals(LOCALIZATION_IMAGE_PATH))  localizationImage = pairData.getValue();
                else metadata.put(pairData.getKey(), new String(pairData.getValue()));
            }
            
//            dataList.forEach(pairData -> metadata.put(pairData.getKey(), pairData.getValue()));
            data.put(getMetadata.getIndexData().getGroupName(), new GetFrontData(getMetadata.getIndexData().getGroupName(), new HashSet<>(), localizationImage, metadata));
        }

        // 查询 topic 并处理
        Result<List<IndexData>> indexsResult = apiService.getIndex(indexData, TOPIC_TYPE);
        if(ResultCode.SUCCESS.getCode() != indexsResult.getCode()){
            log.error("{}: query topic fail", methodName);
            result.setCode(indexsResult.getCode());
            result.setMsg(indexsResult.getMsg());
            return result;
        }
        List<IndexData> indexDataList = indexsResult.getData();
        indexDataList.forEach(index -> {
            if (data.containsKey(index.getGroupName())) {
                data.get(index.getGroupName()).getTopics().add(index.getTopicName());
            }
        });

        // 对数据进行排序
        List<GetFrontData> dataList = new ArrayList<>(data.values());
        Collections.sort(dataList, (o1, o2) -> {
            String[] strings = o1.getGroupName().split("_");
            String left = strings[strings.length - 1];
            strings = o2.getGroupName().split("_");
            String right = strings[strings.length-1];
            return right.compareTo(left);
        });
        result.returnWithValue(ResultCode.SUCCESS, dataList);
        return result;
    }


    @Override
    public Result<Map<String, Object>> queryGroup(GroupDataQueryVO groupDataQueryVO) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: {}", methodName, groupDataQueryVO);
        Result<Map<String, Object>> result = new Result<>();
        IndexData indexData = groupDataQueryVO.getIndexData();
        if(indexData.getGroupName().isEmpty()){
            result.returnWithoutValue(ResultCode.LACK_PARAM_ERROR);
            return result;
        }

        List<String> keyList = groupDataQueryVO.getKeyList();
        // query group
        Result<List<GetMetadata>> groupResult = apiService.getMetadata(indexData,GROUP_TYPE, keyList);
        if(ResultCode.SUCCESS.getCode() != groupResult.getCode()){
            log.error("{}: query group fail", methodName);
            result.returnWithOutValue(groupResult);
            return result;
        }
        List<GetMetadata> groupMetadataList = groupResult.getData();
        if(groupMetadataList.isEmpty()){
            log.error("{}: group metadata list is empty", methodName);
            result.returnWithoutValue(ResultCode.PARAM_ERROR);
            return result;
        }


        Map<String, Object> data = new HashMap<>();
        data.put(GROUP_NAME, groupMetadataList.get(0).getIndexData().getGroupName());
        Map<String, String> metadata = new HashMap<>();
        groupMetadataList.get(0).getData().forEach(pair -> metadata.put(pair.getKey(), new String(pair.getValue())));
        data.put(METADATA, metadata);

        List<BagFrontData> bags = new ArrayList<>();
        // query bag
        Result<List<GetMetadata>> bagResult = apiService.getMetadata(indexData, BAG_TYPE);
        if(ResultCode.SUCCESS.getCode() != bagResult.getCode()){
            log.error("{}: query bag fail", methodName);
            result.returnWithOutValue(bagResult);
            return result;
        }
        List<GetMetadata> bagMetadataList = bagResult.getData();


        // query topic
        Result<List<IndexData>> topicResult = apiService.getIndex(indexData, TOPIC_TYPE);
        if(ResultCode.SUCCESS.getCode() != topicResult.getCode()){
            log.error("{}: query topic fail", methodName);
            result.returnWithOutValue(topicResult);
            return result;
        }
        List<IndexData> topicIndexList = topicResult.getData();
        int topicIndex = 0;

        for (int i = 0; i < bagMetadataList.size(); ++i) {
            GetMetadata bagMetadata = bagMetadataList.get(i);
            String bagName = bagMetadata.getIndexData().getBagName();
            List<Pair<String, byte[]>> pairList = bagMetadata.getData();
            Map<String, String> metadatas = new HashMap<>();
            pairList.forEach(pair -> metadatas.put(pair.getKey(), new String(pair.getValue())));
            BagFrontData bagFrontData = new BagFrontData();
            bagFrontData.setBagName(bagName);
            bagFrontData.setMetadata(metadatas);
            List<String> topics = new ArrayList<>();
            bagFrontData.setTopics(topics);
            bags.add(bagFrontData);
            while(topicIndex < topicIndexList.size() && topicIndexList.get(topicIndex).getBagName().equals(bagName)){
                IndexData topicIndexData = topicIndexList.get(topicIndex);
                topics.add(topicIndexData.getTopicName());
                ++topicIndex;
            }
        }

        data.put("bags", bags);
        result.returnWithValue(ResultCode.SUCCESS, data);
        return result;
    }



    @Transactional(rollbackFor = Exception.class)
    @Retryable(
            value = {MongoException.class, UncategorizedMongoDbException.class},
            backoff = @Backoff(delay = 100L, maxDelay = 3000L, random = true)
    )
    @Override
    public Result updateGroupMetadata(UpdateMetadataVO updateMetadataVO) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        Result result = new Result<>();
        IndexData indexData = new IndexData();
        indexData.setGroupName(updateMetadataVO.getGroupName());
        log.info("{}: {}", methodName, indexData);
        if(indexData.getGroupName().isEmpty()){
            log.error("{}: group name is empty", methodName);
            result.returnWithoutValue(ResultCode.LACK_PARAM_ERROR);
            return result;
        }

        List<Pair<String, byte[]>> metadataList = new ArrayList<>();
        Map<String, String> metadatas = updateMetadataVO.getMetadata();
        metadatas.remove(CREATE_TIME);
        metadatas.remove(UPDATE_TIME);
        metadatas.forEach((key, value) -> metadataList.add(new Pair<>(key, value.getBytes())));
        Result<Long> longResult = apiService.saveMetadata(indexData, GroupData.class, GROUP_TYPE, metadataList);
        if(longResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: update metadata error", methodName);
            result.returnWithOutValue(longResult);
            return result;
        }
        if(metadatas.containsKey(RECORD))
            result = apiForTaskService.generateBagRecordByName(updateMetadataVO.getGroupName());
        else
            result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }


    // user


    @Override
    public Result<byte[]> getPointCloudImage(@NotBlank String groupName, @NotBlank String bagName, @NotBlank String topicName, @Min(1) int frameNo) {
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}, bag name: {}, topic name: {}, frame no: {}", methodName, bagName, topicName, frameNo);
        Result<byte[]> result = new Result<>();
        IndexData indexData = new IndexData(groupName, bagName, topicName, frameNo);
        Result<List<GetMetadata>> metadataResult = apiService.getMetadata(indexData, FRAME_TYPE, PATH);

        if(metadataResult.getCode() != ResultCode.SUCCESS.getCode()){
            log.error("{}: query frame metadata fail", methodName);
            result.returnWithOutValue(metadataResult);
            return result;
        }
        List<GetMetadata> getMetadataList = metadataResult.getData();
        if(getMetadataList.isEmpty() || getMetadataList.get(0).getData().isEmpty()){
            log.error("{}: no such group", methodName);
            result.returnWithoutValue(ResultCode.NO_SUCH_FILE);
            return result;
        }
        byte[] bytes = getMetadataList.get(0).getData().get(0).getValue();
        byte[] encodeBase64 = Base64.encodeBase64(bytes);
        result.returnWithValue(ResultCode.SUCCESS, encodeBase64);
        return result;
    }


































    // -----------------------------------------------------------------------------------------------------------------

    // bag record
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Long> temporaryFunction1() {
        Result<Long> result = new Result<>();
        List<GroupData> groupDataList = mongoOperations.findAll(GroupData.class);
        groupDataList.forEach(groupData -> apiForTaskService.generateBagRecordByName(groupData.getGroupName()));
        return result;
    }

    // baidu gps
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Long> temporaryFunction2() {
        Result<Long> result = new Result<>();
        Query query = new Query();
        query.addCriteria(Criteria.where(COMPLETE_STATUS).is(PROCESSED_STATUS));
        query.with(new Sort(Sort.Direction.ASC, "_id"));
        List<GroupDataTask> groupDataTasksList = mongoOperations.find(query, GroupDataTask.class);

        groupDataTasksList.forEach(groupData -> {
                Result result1 = apiForTaskService.generateBagGpsByName(groupData.getGroupName());
                if(result1.getCode() != ResultCode.SUCCESS.getCode()){
                    log.error("temporaryFunction2: generate bag gps error");
                    throw new RuntimeException("generate bag gps error");
                }
        });

        result.returnWithoutValue(ResultCode.SUCCESS);
        return result;
    }

    // take over
//    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Long> temporaryFunction3() {
        List<GroupDataTask> groupDataTasksList = mongoOperations.findAll(GroupDataTask.class);
        groupDataTasksList.forEach(groupDataTask -> takeOverChange(groupDataTask.getGroupName()));
        return new Result<>();
    }


    private Result takeOverChange(@NotBlank String groupName){
        String methodName = new Exception().getStackTrace()[0].getMethodName();
        log.info("{}: group name: {}", methodName, groupName);
        Query query = new Query();
        query.addCriteria(Criteria.where(GROUP_NAME).is(groupName));
        GroupData groupData = mongoOperations.findOne(query, GroupData.class);
        byte[] bytes = groupData.getMetadata().get(RECORD);
        String record = new String(bytes);
        List<Object> objectList = JSONUtil.convertJSonString2List(record);
        for (int i = 0; i < objectList.size(); i++) {
            Object object = objectList.get(i);
            List<String> categoryList = (List<String>) ((Map) object).get(CATEGORY);
            boolean isTakeOVer = false;
            for (String data : categoryList) {
                if(data.equals("TakeOver/TakeOver"))
                    isTakeOVer = true;
            }
//            categoryList.remove("TakeOver/TakeOver");
            ((Map<String, Object>) object).put(TAKE_OVER, isTakeOVer);
        }

        IndexData groupIndexData = new IndexData();
        groupIndexData.setGroupName(groupName);
        // group  // bag
        bytes = JSONUtil.convertList2Bytes(objectList);
        Map<String, String> metadata = new HashMap<>();
        metadata.put(RECORD, new String(bytes));
        UpdateMetadataVO updateMetadataVO = new UpdateMetadataVO();
        updateMetadataVO.setGroupName(groupName);
        updateMetadataVO.setMetadata(metadata);
        Result result = updateGroupMetadata(updateMetadataVO);
        if(result.getCode() != ResultCode.SUCCESS.getCode()) {
            log.warn("{}: update group record fail. {}", methodName, result.getMsg());
        }
        return result;
    }




//    @Retryable(
//            value = Exception.class,
//            maxAttempts = 10,
//            backoff = @Backoff(delay = 100L, maxDelay = 3000L, random = true)
//    )
//    @Transactional(rollbackFor = Exception.class)
//    public Result generateBagGpsByName(@NotBlank String groupName){
//        String methodName = new Exception().getStackTrace()[0].getMethodName();
//        log.info("{}: group name: {}", methodName, groupName);
//        Result result = new Result();
//        IndexData indexData = new IndexData();
//        indexData.setGroupName(groupName);
//        Result<List<IndexData>> indexResult = apiService.getIndex(indexData, BAG_TYPE);
//        List<IndexData> indexDataList = indexResult.getData();
//
//        List<Callable<Result>> callableList = new ArrayList<>();
//        for (IndexData data : indexDataList) {
//            data.setTopicName(TOPIC_ROADSTAR_LOCALIZATION);
//            data.setFrameNo(FRAME_FIRST_FRAME);
//            callableList.add(() -> {
//                Result<List<GetMetadata>> metadataResult = apiService.getMetadata(data, FRAME_TYPE, PATH);
//                if (metadataResult.getData().isEmpty()){
//                    log.warn("{}: bag name: {}, no such frame", methodName, data.getBagName());
//                    return metadataResult;
//                }
//                GetMetadata getMetadata = metadataResult.getData().get(0);
//                if(getMetadata.getData().isEmpty()){
//                    log.warn("{}: bag : {} no localization", methodName, data.getBagName());
//                    return new Result(ResultCode.SUCCESS);
//                }
//                List<Position> pointList = FileUtil.readLocalizationDataByBytes(getMetadata.getData().get(0).getValue(), BAG_GPS_INTERVAL);
//                List<Position> baiduPointList = apiForTaskService.utm2BaiduGps(pointList);
//                return apiService.saveMetadata(data, BagData.class, BAG_TYPE, new Pair<>(GPS, baiduPointList.toString().getBytes()));
//            });
//        }
//        try {
//            ThreadManager.invokeAll(callableList);
//        } catch (InterruptedException e) {
//            throw new MongoException("thread interrupt and retry again");
//        }
//
//
//        result.returnWithoutValue(ResultCode.SUCCESS);
//        return result;
//    }




//    // generate static map and mileage
//    @Retryable(
//            value = {Exception.class},
//            maxAttempts = 10,
//            backoff = @Backoff(delay = 100L, maxDelay = 3000L, random = true)
//    )
//    @Transactional(rollbackFor = Exception.class)
//    @Override
//    public Result generateGroupMapByName(@NotBlank String groupName) {
//        log.info("generateGroupMap: group name: {}", groupName);
//        Result<Boolean> result = new Result<>();
//        IndexData indexData = new IndexData();
//        indexData.setGroupName(groupName);
//        Result<List<IndexData>> bagIndexResult = apiService.getIndex(indexData, BAG_TYPE);
//        if(bagIndexResult.getCode() != ResultCode.SUCCESS.getCode()){
//            log.error("generateGroupMapByName: query bag index error. msg: {}", bagIndexResult.getMsg());
//            throw new RuntimeException(bagIndexResult.getMsg());
//        }
//
//        List<IndexData> bagIndexList = bagIndexResult.getData();
//
//        List<Position> utmList = new ArrayList<>();
//        List<List<Position>> mileageUtmLists = new ArrayList<>();
//        for (IndexData bagIndex : bagIndexList) {
//            IndexData bagIndexData = new IndexData(groupName, bagIndex.getBagName(), TOPIC_ROADSTAR_LOCALIZATION, FRAME_FIRST_FRAME);
//            Result<List<GetMetadata>> listResult = apiService.getMetadata(bagIndexData, FRAME_TYPE, PATH);
//
//            if(listResult.getCode() != ResultCode.SUCCESS.getCode()){
//                log.error("generateGroupMap: bag name: {}, msg: {}", bagIndex.getBagName(), listResult.getMsg());
//                mileageUtmLists.add(new ArrayList<>());
//                continue;
//            }
//            List<GetMetadata> metadataList = listResult.getData();
//            if (metadataList.isEmpty()){
//                log.warn("generateGroupMap: bag name: {}, no such frame", bagIndex.getBagName());
//                mileageUtmLists.add(new ArrayList<>());
//                continue;
//            }
//
//            List<Pair<String, byte[]>> data = metadataList.get(0).getData();
//            if(data.isEmpty()){
//                log.warn("generateGroupMap: bag name: {}, no localization", bagIndex.getBagName());
//                mileageUtmLists.add(new ArrayList<>());
//                continue;
//            }
//            byte[] localizationValue = data.get(0).getValue();
////            utmList.addAll(FileUtil.readLocalizationDataByBytes(localizationValue, pointInterval));
////            mileageUtmLists.add(FileUtil.readLocalizationDataByBytes(localizationValue, mileagePointInterval));
//        }
//
//        String saveFile = bagSavePath + groupName + "/localization.png";
//        if(utmList.isEmpty()){
//            log.warn("generateGroupMap: no image to generate (no bags)");
//        }
//
//        List<Position> utmListGap = new ArrayList<>();
//        int gap = utmList.size() / 200 + 1;
//        if (gap != 1) {
//            for (int i = 0; i < utmList.size() - 1; i += gap) {
//                utmListGap.add(utmList.get(i));
//            }
//            utmListGap.add(utmList.get(utmList.size() - 1));
//        } else {
//            utmListGap = utmList;
//        }
//
//
//        int mapResult = initMap(utmListGap, saveFile);
//        double groupMileage = calculateMileage(mileageUtmLists, bagIndexList, groupName);
//        if (mapResult != 0) {
//            throw new RuntimeException("generateGroupMap: write image to file error!");
//        }
//        IndexData groupIndexData = new IndexData();
//        groupIndexData.setBagName(groupName);
//        Result<Long> groupResult = apiService.saveMetadata(groupIndexData, GroupData.class, GROUP_TYPE, new Pair<>(LOCALIZATION_IMAGE_PATH, saveFile.getBytes()), new Pair<>(MILEAGE, Double.toString(groupMileage).getBytes()));
//
//        if(groupResult.getData() == 0){
//            log.error("generateGroupMap: group data is null");
//            throw new RuntimeException("generateGroupMap: group data is null");
//        }
//        log.info("generateGroupMap: gps map generate successful");
//        result.returnWithoutValue(ResultCode.SUCCESS);
//        return result;
//    }
//




}

